/*
 * Copyright (c) 2020-2099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.utils;

import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 数据替换工具类，用于对数据进行替换，E可以是任意类型，建议使用基础数据类型，比如：String、Integer等。如果使用自定义类，需要重写类的equals方法。
 * <p>
 * <p>
 * 示例代码： <code>
 * List<String> list1 = Arrays.asList("111.jpg", "222.jpg");
 * List<String> list2 = Arrays.asList("333.jpg", "444.jpg");
 * <p>
 * Function<List<String>, Map<String, String>> provider = list -> {
 * return list.stream().collect(Collectors.toMap(Function.identity(), uri -> {
 * return "http://image.xxx.com/" + uri;
 * } , (o1, o2) -> o1));
 * };
 * <p>
 * SwapBuilder<String> builder = new SwapBuilder<>();
 * builder.provider(provider)
 * .list(productRequest, ProductRequest::getHeadImg, ProductRequest::setHeadImg)
 * .list(productRequest.getDescInfo(), ProductDescInfoRequest::getImgs, ProductDescInfoRequest::setImgs)
 * .map(productRequest.getSkus(), ProductSkuRequest::getThumbImg, ProductSkuRequest::setThumbImg);
 * boolean state = builder.swap();
 * <p>
 * 转换前结果：{"headImg":["111.jpg","222.jpg"],"qualificationPics":["333.jpg","444.jpg"],"skus":[{"thumbImg":"555.jpg"},{"thumbImg":"666.jpg"}]}
 * 转换后结果：{"headImg":["http://image.xxx.com/111.jpg","http://image.xxx.com/222.jpg"],"qualificationPics":["http://image.xxx.com/333.jpg","http://image.xxx.com/444.jpg"],"skus":[{"thumbImg":"http://image.xxx.com/555.jpg"},{"thumbImg":"http://image.xxx.com/666.jpg"}]}
 * </code>
 *
 * @auther ebert_chan
 */
public class SwapBuilder<E> {

    private Function<List<E>, Map<E, E>> provider;

    private List<SwapPattern<Object>> patterns = new ArrayList<>();

    public SwapBuilder() {
    }

    /**
     * 设置数据交换的提供者
     *
     * @param provider
     * @return
     * @auther ebert_chan
     */
    public SwapBuilder<E> provider(Function<List<E>, Map<E, E>> provider) {
        this.provider = provider;
        return this;
    }

    /**
     * 设置要交换数据的对象及属性
     *
     * @param object               需要交换数据的对象
     * @param objectPropertyGetter 对象属性的Getter方法
     * @param objectPropertySetter 对象属性的Setter方法
     * @return
     * @auther ebert_chan
     */
    @SuppressWarnings("unchecked")
    public <T> SwapBuilder<E> map(T object, Function<T, E> objectPropertyGetter, BiConsumer<T, E> objectPropertySetter) {
        SwapPattern<T> pattern = new SwapPattern<>();
        pattern.setObject(object);
        pattern.setObjectPropertyGetter(objectPropertyGetter);
        pattern.setObjectPropertySetter(objectPropertySetter);
        patterns.add((SwapPattern<Object>) pattern);
        return this;
    }

    /**
     * 设置要交换数据的集合对象及属性
     *
     * @param iterable             需要交换数据的集合对象
     * @param objectPropertyGetter 集合内对象属性的Getter方法
     * @param objectPropertySetter 集合内对象属性的Setter方法
     * @return
     * @auther ebert_chan
     */
    @SuppressWarnings("unchecked")
    public <T> SwapBuilder<E> map(List<T> iterable, Function<T, E> objectPropertyGetter, BiConsumer<T, E> objectPropertySetter) {
        SwapPattern<T> pattern = new SwapPattern<>();
        pattern.setIterable(iterable);
        pattern.setObjectPropertyGetter(objectPropertyGetter);
        pattern.setObjectPropertySetter(objectPropertySetter);
        patterns.add((SwapPattern<Object>) pattern);
        return this;
    }

    /**
     * 设置要交换数据的对象及属性
     *
     * @param object                 需要交换数据的对象
     * @param iterablePropertyGetter 集合内对象属性的Getter方法
     * @param iterablePropertySetter 集合内对象属性的Setter方法
     * @return
     * @auther ebert_chan
     */
    @SuppressWarnings("unchecked")
    public <T> SwapBuilder<E> list(T object, Function<T, List<E>> iterablePropertyGetter, BiConsumer<T, List<E>> iterablePropertySetter) {
        SwapPattern<T> pattern = new SwapPattern<>();
        pattern.setObject(object);
        pattern.setIterablePropertyGetter(iterablePropertyGetter);
        pattern.setIterablePropertySetter(iterablePropertySetter);
        patterns.add((SwapPattern<Object>) pattern);
        return this;
    }

    /**
     * 设置要交换数据的集合对象及属性
     *
     * @param iterable               需要交换数据的集合对象
     * @param iterablePropertyGetter 集合内对象属性的Getter方法
     * @param iterablePropertySetter 集合内对象属性的Setter方法
     * @return
     * @auther ebert_chan
     */
    @SuppressWarnings("unchecked")
    public <T> SwapBuilder<E> list(List<T> iterable, Function<T, List<E>> iterablePropertyGetter, BiConsumer<T, List<E>> iterablePropertySetter) {
        SwapPattern<T> pattern = new SwapPattern<>();
        pattern.setIterable(iterable);
        pattern.setIterablePropertyGetter(iterablePropertyGetter);
        pattern.setIterablePropertySetter(iterablePropertySetter);
        patterns.add((SwapPattern<Object>) pattern);
        return this;
    }

    /**
     * 对数据进行补全
     * <p>
     *
     * @author ebert_chan
     */
    public void fill() {
        List<E> original = this.collect();
        if (!original.isEmpty()) {
            Map<E, E> result = provider.apply(original);
            if (result == null || result.isEmpty()) {
                return;
            }
            for (SwapPattern<Object> pattern : patterns) {
                setValue(pattern, result, pattern.getObject());
                if (pattern.getIterable() != null) {
                    pattern.getIterable().forEach(obj -> {
                        setValue(pattern, result, obj);
                    });
                }
            }
        }
    }

    /**
     * 开始进行数据替换
     *
     * @return
     * @auther ebert_chan
     */
    public boolean swap() {
        List<E> original = this.collect();
        if (!original.isEmpty()) {
            Map<E, E> result = provider.apply(original);
            if (result == null || original.size() != result.size()) {
                return false;
            }
            for (E key : original) {
                if (!result.containsKey(key)) {
                    return false;
                }
            }

            for (SwapPattern<Object> pattern : patterns) {
                setValue(pattern, result, pattern.getObject());
                if (pattern.getIterable() != null) {
                    pattern.getIterable().forEach(obj -> {
                        setValue(pattern, result, obj);
                    });
                }
            }

        }
        return true;
    }

    private void setValue(SwapPattern<Object> pattern, Map<E, E> result, Object obj) {
        if (obj == null) {
            return;
        }
        if (pattern.getObjectPropertyGetter() != null) {
            E key = (E) pattern.getObjectPropertyGetter().apply(obj);
            if (Objects.nonNull(key)) {
                E data = result.get(key);
                if (Objects.nonNull(data)) {
                    pattern.getObjectPropertySetter().accept(obj, data);
                }
            }
        }
        if (pattern.getIterablePropertyGetter() != null) {
            List<E> keys = (List<E>) pattern.getIterablePropertyGetter().apply(obj);
            if (!CollectionUtils.isEmpty(keys)) {
                List<E> list = new ArrayList<>(keys.size());
                for (E key : keys) {
                    E data = result.get(key);
                    if (Objects.nonNull(data)) {
                        list.add(data);
                    }
                }
                pattern.getIterablePropertySetter().accept(obj, list);
            }
        }
    }

    /**
     * 归集数据
     *
     * @return
     * @auther ebert_chan
     */
    private List<E> collect() {
        Set<E> original = new HashSet<>();
        for (SwapPattern<Object> transformCondition : patterns) {
            collect(transformCondition, original, transformCondition.getObject());
            if (transformCondition.getIterable() != null) {
                transformCondition.getIterable().forEach(obj -> {
                    collect(transformCondition, original, obj);
                });
            }
        }
        return original.stream().collect(Collectors.toList());
    }

    private void collect(SwapPattern<Object> pattern, Set<E> original, Object obj) {
        if (obj == null) {
            return;
        }
        if (pattern.getObjectPropertyGetter() != null) {
            E data = (E) pattern.getObjectPropertyGetter().apply(obj);
            if (Objects.nonNull(data)) {
                original.add(data);
            }
        }
        if (pattern.getIterablePropertyGetter() != null) {
            List<E> data = (List<E>) pattern.getIterablePropertyGetter().apply(obj);
            if (!CollectionUtils.isEmpty(data)) {
                original.addAll(data);
            }
        }
    }

    /**
     * 交换规则定义
     *
     * @auther ebert_chan
     */
    private class SwapPattern<T> {

        private List<T> iterable;

        private T object;

        private Function<T, E> objectPropertyGetter;

        private BiConsumer<T, E> objectPropertySetter;

        private Function<T, List<E>> iterablePropertyGetter;

        private BiConsumer<T, List<E>> iterablePropertySetter;

        public List<T> getIterable() {
            return iterable;
        }

        public void setIterable(List<T> iterable) {
            this.iterable = iterable;
        }

        public T getObject() {
            return object;
        }

        public void setObject(T object) {
            this.object = object;
        }

        public Function<T, E> getObjectPropertyGetter() {
            return objectPropertyGetter;
        }

        public void setObjectPropertyGetter(Function<T, E> objectPropertyGetter) {
            this.objectPropertyGetter = objectPropertyGetter;
        }

        public BiConsumer<T, E> getObjectPropertySetter() {
            return objectPropertySetter;
        }

        public void setObjectPropertySetter(BiConsumer<T, E> objectPropertySetter) {
            this.objectPropertySetter = objectPropertySetter;
        }

        public Function<T, List<E>> getIterablePropertyGetter() {
            return iterablePropertyGetter;
        }

        public void setIterablePropertyGetter(Function<T, List<E>> iterablePropertyGetter) {
            this.iterablePropertyGetter = iterablePropertyGetter;
        }

        public BiConsumer<T, List<E>> getIterablePropertySetter() {
            return iterablePropertySetter;
        }

        public void setIterablePropertySetter(BiConsumer<T, List<E>> iterablePropertySetter) {
            this.iterablePropertySetter = iterablePropertySetter;
        }

    }

}
